﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;

namespace ShevaEngine.Core.Modules.Materials
{
    /// <summary>
    /// Material manager.
    /// </summary>
    public class MaterialManager
    {
        /// <summary>Singleton.</summary>
        public static readonly MaterialManager Instance = new MaterialManager();

        /// <summary>Materials.</summary>
        public List<Material> Materials;
        /// <summary>Material IDs.</summary>
        private SortedDictionary<string, UInt16> MaterialIDs;
        /// <summary>Effects.</summary>
        private SortedDictionary<string, Effect> Effects;
        /// <summary>BlendStates.</summary>
        private List<BlendState> BlendStates;
        /// <summary>Depth stencil states.</summary>
        private List<DepthStencilState> DepthStencilStates;

        /// <summary>
        /// Material.
        /// </summary>
        /// <param name="materialName"></param>
        /// <returns></returns>
        public Material this[UInt16 materialId]
        {
            get
            {
                if (this.Materials.Count > materialId)
                    return this.Materials[materialId];

                return null;
            }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        private MaterialManager()
        {
            this.Materials = new List<Material>();
            this.MaterialIDs = new SortedDictionary<string, ushort>();
            this.Effects = new SortedDictionary<string, Effect>();
            this.BlendStates = new List<BlendState>();
            this.DepthStencilStates = new List<DepthStencilState>();
        }

        /// <summary>
        /// Method returns Material ID.
        /// </summary>
        /// <param name="materialName"></param>
        /// <returns></returns>
        public UInt16 GetMaterialID(string materialName)
        {
            if (!this.MaterialIDs.ContainsKey(materialName))
            {
                try
                {
                    Material newMaterial = ShevaEngine.Instance.Content.Load<Material>(@"Materials\" + materialName);

                    this.MaterialIDs.Add(materialName, (UInt16)this.Materials.Count);

                    newMaterial.Init();

                    this.Materials.Add(newMaterial);
                }
                catch
                {
                    return this.GetMaterialID("MaterialNormalMap");
                }
            }

            return this.MaterialIDs[materialName];
        }

        /// <summary>
        /// Method gets effect with name.
        /// </summary>
        /// <param name="effectName"></param>
        /// <returns></returns>
        public Effect GetEffect(string effectName)
        {
            if (!this.Effects.ContainsKey(effectName))
                this.Effects.Add(effectName, ShevaEngine.Instance.Content.Load<Effect>("Effects\\" + effectName));

            return this.Effects[effectName];
        }

        /// <summary>
        /// Method returns BlendState.
        /// </summary>
        /// <param name="alphaBlendFunction"></param>
        /// <param name="alphaSourceBlend"></param>
        /// <param name="alphaDestinationBlend"></param>
        /// <returns></returns>
        public BlendState GetBlendState(BlendFunction alphaBlendFunction, Blend alphaSourceBlend, Blend alphaDestinationBlend)
        {
            BlendState blendState = this.BlendStates.FirstOrDefault<BlendState>(item =>
                item.AlphaBlendFunction == alphaBlendFunction && 
                item.AlphaSourceBlend == alphaSourceBlend &&
                item.AlphaDestinationBlend == alphaDestinationBlend);

            if (blendState == null)
            {
                blendState = new BlendState();
                blendState.AlphaBlendFunction = alphaBlendFunction;
                blendState.AlphaSourceBlend = alphaSourceBlend;
                blendState.AlphaDestinationBlend = alphaDestinationBlend;

                blendState.ColorBlendFunction = blendState.AlphaBlendFunction;
                blendState.ColorSourceBlend = blendState.AlphaSourceBlend;
                blendState.ColorDestinationBlend = blendState.AlphaDestinationBlend;

                this.BlendStates.Add(blendState);
            }

            return blendState;
        }

        /// <summary>
        /// Method return DepthStencilState.
        /// </summary>
        /// <param name="depthBufferEnable"></param>
        /// <param name="depthBufferFunction"></param>
        /// <param name="depthBufferWriteEnable"></param>
        /// <returns></returns>
        public DepthStencilState GetDepthStencilState(bool depthBufferEnable, CompareFunction depthBufferFunction, 
            bool depthBufferWriteEnable)
        {
            DepthStencilState depthStencilState = this.DepthStencilStates.FirstOrDefault<DepthStencilState>(item =>
                item.DepthBufferEnable == depthBufferEnable &&
                item.DepthBufferFunction == depthBufferFunction &&
                item.DepthBufferWriteEnable == depthBufferWriteEnable);

            if (depthStencilState == null)
            {
                depthStencilState = new DepthStencilState();
                depthStencilState.DepthBufferEnable = depthBufferEnable;
                depthStencilState.DepthBufferFunction = depthBufferFunction;
                depthStencilState.DepthBufferWriteEnable = depthBufferWriteEnable;

                this.DepthStencilStates.Add(depthStencilState);
            }

            return depthStencilState;
        }
    }
}
